/***************************************************************************
 *   Copyright (C) 2008 by Łukasz Spintzyk   *
 *   lukasz.spintzyk@gmail.com   *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/
#include "control.h"



enum selectedItem selectNum=NONE;
uint8_t setNum=0;
upoint8_t textCursorPos={0,0};

/*! Function checks state of buttons of keyboard(Which is active), and signals when the value of Analog key is valid.
    \return 8-bit value in format [ 000 | (Analog_key valid value- 1 bit) | (4KEYS - 4 bits) ]
 */
uint8_t checkButtons()
{
    static uint8_t prevButtons=ACTIVE;
    uint8_t temp=prevButtons;		/// Remember values, from last invoke of checkButtons()
    //! \todo Check ADC_conversion & ADC_AKEY works propely
    prevButtons=PIN(KEYBOARD);

    return ~prevButtons&temp & KEYBOARD_MASK;
}

/*! Function is invoked when user press SELECT button.
 */
void selectClicked()
{
    selectNum++;
    if (setNum) {
        setNum=0;
        showTextCursor(false,1,false,0,0);
        runADC_AKey(false);//turn off ADC conversion of Analog key
    }
    setFunction=0;
    switch (selectNum) {
        case SET_VOLT_DIV://! constants are used in order to frealy modify options order in menu
            setFunction=setVoltDiv;
            showTextCursor(true,1,false,VOLTPERDIV_X-1,VOLTPERDIV_Y);
            textCursorPos.x=VOLTPERDIV_X-1;
            textCursorPos.y=VOLTPERDIV_Y;
            break;
        case SET_TIME_DIV:
            setFunction=setTimeDiv;
            showTextCursor(true,1,false,TIMEPERDIV_X-1,TIMEPERDIV_Y);
            textCursorPos.x=TIMEPERDIV_X-1;
            textCursorPos.y=TIMEPERDIV_Y;
            break;
        case SET_CH1:
            setFunction=setState;
            showTextCursor(true,1,false,CH1_X-1,CH1_Y);
            textCursorPos.x=CH1_X-1;
            textCursorPos.y=CH1_Y;
            runADC_AKey(true);
            break;
        case SET_CH2:
            setFunction=setState;
            showTextCursor(true,1,false,CH2_X-1,CH2_Y);
            textCursorPos.x=CH2_X-1;
            textCursorPos.y=CH2_Y;
           runADC_AKey(true);
            break;
        case SET_ADD:
            setFunction=setState;
            showTextCursor(true,1,false,ADD_X-1,ADD_Y);
            textCursorPos.x=ADD_X-1;
            textCursorPos.y=ADD_Y;
            runADC_AKey(true);
            break;
        case SET_SUB:
            setFunction=setState;
            showTextCursor(true,1,false,SUB_X-1,SUB_Y);
            textCursorPos.x=SUB_X-1;
            textCursorPos.y=SUB_Y;
            runADC_AKey(true);
            break;
        case SET_ACDC:
            setFunction=setState;
            showTextCursor(true,1,false,AC_X-1,AC_Y);
            textCursorPos.x=AC_X-1;
            textCursorPos.y=AC_Y;
            break;
        case SET_CURSOR_X1:
            setFunction=setCursor;
            showTextCursor(true,1,false,CURSORX1_X-1,CURSOR_Y);
            textCursorPos.x=CURSORX1_X-1;
            textCursorPos.y=CURSOR_Y;
            runADC_AKey(true);
            break;
        case SET_CURSOR_X2:
            setFunction=setCursor;
            showTextCursor(true,1,false,CURSORX2_X-1,CURSOR_Y);
            textCursorPos.x=CURSORX2_X-1;
            textCursorPos.y=CURSOR_Y;
            runADC_AKey(true);
            break;
        case SET_CURSOR_Y1:
            setFunction=setCursor;
            showTextCursor(true,1,false,CURSORY1_X-1,CURSOR_Y);
            textCursorPos.x=CURSORY1_X-1;
            textCursorPos.y=CURSOR_Y;
            runADC_AKey(true);
            break;

        case SET_CURSOR_Y2:
            setFunction=setCursor;
            showTextCursor(true,1,false,CURSORY2_X-1,CURSOR_Y);
            textCursorPos.x=CURSORY2_X-1;
            textCursorPos.y=CURSOR_Y;
            runADC_AKey(true);
            break;
        case SET_FREQ_ADJUST:
            setFunction=setState;//! Wybierz funkcj� do zmiany pseudo okresu podstawy czasu.
            showTextCursor(true,1,false,FR_X-1,FR_Y);
            textCursorPos.x=FR_X-1;
            textCursorPos.y=FR_Y;
            runADC_AKey(true);
            break;

        default:
            showTextCursor(false,1,false,0,0);
            textCursorPos.x=0;
            textCursorPos.y=0;
            runADC_AKey(false);
            selectNum=NONE;
            setFunction=0;
            setNum=0;
            break;
    }
    
}


/*!Function change Voltage div settings and display.
 * \param param value which means up or down
 * \param a if true then param value comes from adc_key
 */
uint8_t setVoltDiv(int8_t param, bool a)
{
    if (a==true) { // param is value vrom adc so we get out.
        return 0;
    }
    cli();        //block interrupts
    
    curVoltOpt+=param;
    
    if (curVoltOpt>=VOLT_OPT_NUM) curVoltOpt=VOLT_OPT_NUM-1;
    if (curVoltOpt<0) curVoltOpt=0;
    
    display_text(voltText[curVoltOpt],VOLTPERDIV_VALUE_X,VOLTPERDIV_Y);//display current volt per div value

    //change control bits. STA0, STA1, STB0, STB1
    uint8_t variable;
    memcpy_P(&variable, &stab[curVoltOpt], sizeof(uint8_t));//read data from table placed in ram
    PORT(CONTROL)&=(uint8_t)STAB_MASK;
    PORT(CONTROL)|=variable;
    
    setCursor(0,false);//update cursor value
    sei();//unlock interrupts
    
    return 0;
}

uint8_t setTimeDiv(int8_t param, bool a)
{
    if (a) { // param is value from adc converter, we meed data only from keyboard + and -
        return 0;
    }
    cli();
    curTimeOpt+=param;
    if (curTimeOpt>=TIME_OPT_NUM) curTimeOpt=TIME_OPT_NUM-1;
    if (curTimeOpt<0) curTimeOpt=0;

    display_text(timeText[curTimeOpt],TIMEPERDIV_VALUE_X,TIMEPERDIV_Y);//display current seconds per div value
    
    //change timer settings to timeValue[curTimeOpt]/TDIV_W/sampleNum[curTimeOpt]
    uint16_t timeDiv;
    uint8_t samples;
    memcpy_P(&timeDiv, &timeValue[curTimeOpt], sizeof(uint16_t));//read data from table placed in ram
    memcpy_P(&samples, &sampleNum[curTimeOpt], sizeof(uint8_t));
    
    timeDiv=timeDiv/(TDIV_W*samples);
    if (curTimeOpt<=US_END) 
        setTime_us(timeDiv);
    else if (curTimeOpt>=MS_BEGIN && curTimeOpt<=MS_END)
        setTime_ms(timeDiv);
    else if (curTimeOpt>=S_BEGIN)
        setTime_ms(timeDiv*1000);
    
    setCursor(0,false);//update cursor value
    sei();

    return 0;
}


uint8_t setState(int8_t param, bool a){
    static int8_t oldparam=0;
    if (a){
        if (oldparam==param) return 0;
        else oldparam=param;
    }
    
    switch (selectNum) {
        case SET_ADD:
            if ((DEV_state & (1<<ISADD)) && a) chAddPos=param;
            else {
                cli();//block interrupts
                if(!a){
                    if (param<0) DEV_state&=~(1<<ISADD);    //turn off ADD channel if user pressed -
                    if (param>0) DEV_state|=(1<<ISADD);     //turn on ADD channel if user pressed +
                }

                display_text_PGM((DEV_state & (1<<ISADD)) ? PSTR("ADD ") : PSTR(" *  "),ADD_X,ADD_Y);
                
                sei();
            }
            break;
        case SET_SUB:
            if ((DEV_state & (1<<ISSUB)) && a) chSubPos=param;
            else {
                cli();//zablokuj przerwania na czas aktualizacji ekranu i stanu urz�dzenia
                if (!a){
                    if (param<0) DEV_state&=~(1<<ISSUB);    //turn off SUB channel if user pressed -
                    if (param>0) DEV_state|=(1<<ISSUB);     //turn on SUB channel if user pressed +
                }
                
                display_text_PGM((DEV_state & (1<<ISSUB)) ? PSTR("SUB ") : PSTR(" *  "),SUB_X,SUB_Y);
           
                sei();
            }
            break;
        case SET_CH2:
//! \todo Channel 2 can be turned on only when time div will be greater or equal 200uss,
//! \todo Gdy TB to nie ma potrzeby wy��czania ADD I SUB
            if ((DEV_state & (1<<ISCH2)) && a) ch2Pos=param;    //je�li kana� 2 w��czonu do przesu� jego mas�.
            else if (a && (DEV_state & (1<<ISTB))) ; // !! Nic nie r�b, nie wykorzystane
            else if (!a) { //change state  CH2->TB->*->CH2
                cli();


                if (DEV_state & (1<<ISCH2)) {
                    display_text_PGM(PSTR("TB "),CH2_X,CH2_Y);
                    DEV_state|=(1<<ISTB);
                    DEV_state&=~(1<<ISCH2);
                    DEV_state&=~(1<<ISTRG);
                    getTime=getTimeFromCH2;
                } else if (DEV_state & (1<<ISTB)) {
                    display_text_PGM(PSTR("TRG"),CH2_X,CH2_Y);
                    DEV_state&=~(1<<ISTB);
                    DEV_state&=~(1<<ISCH2);
                    DEV_state|=(1<<ISTRG);
                    getTime=getTimeFromTimerWithTriger;
                } else if ((DEV_state & (1<<ISTRG))) {
                    display_text_PGM(PSTR(" * "),CH2_X,CH2_Y);
                    DEV_state&=~(1<<ISTB);
                    DEV_state&=~(1<<ISCH2);
                    DEV_state&=~(1<<ISTRG);
                    getTime=getTimeFromTimer;
                } else {
                    display_text_PGM(PSTR("CH2 "),CH2_X,CH2_Y);
                    DEV_state|=(1<<ISCH2);
                    DEV_state&=~(1<<ISTB);
                    DEV_state&=~(1<<ISTRG);
                    getTime=getTimeFromTimer;
                }

                sei();
            }
            break;
        case SET_CH1:
            if ((DEV_state & (1<<ISCH1|1<<ISHV)) && a) ch1Pos=param;    //if channel is opened move it gnd level adding to it param value.
            else if (!a) { //trzeba zmieni� stan na CH2->TB->*->CH2
                cli();

                if (DEV_state & (1<<ISCH1)) {
                    display_text_PGM(PSTR("CH1-HV "),CH1_X,CH1_Y);
                    DEV_state|=(1<<ISHV);
                    DEV_state&=~(1<<ISCH1);
                } else if (DEV_state & (1<<ISHV)) {
                    display_text_PGM(PSTR("CH1    "),CH1_X,CH1_Y);
                    DEV_state|=(1<<ISCH1);
                    DEV_state&=~(1<<ISHV);
                }
                else {
                    display_text_PGM(PSTR("CH1    "),CH1_X,CH1_Y);
                    DEV_state|=(1<<ISCH1);
                    DEV_state&=~(1<<ISHV);
                }

                sei();
            }
            break;
        case SET_ACDC:
            cli();

            if (DEV_state & (1<<ISAC)) {
                display_text_PGM(PSTR("DC "),AC_X,AC_Y);

                DEV_state&=~(1<<ISAC);
            } else {
                display_text_PGM(PSTR("AC "),AC_X,AC_Y);
                DEV_state|=(1<<ISAC);
            }

            sei();

            break;
        case SET_FREQ_ADJUST:
            if (a) adjustFreq=param;
            break;

    };
    return 0;
}

uint8_t setCursor(int8_t param, bool a)
{
    static int16_t timeCursor=0;
    static int16_t voltageCursor=0;
    static int8_t oldparam=0;
    if (a){
        if (oldparam==param) return 0;
        else oldparam=param;
    }

    switch (selectNum) {
        case SET_CURSOR_X1:
            if (a) cursor1.x=param;
            else cursor1.x+=param;

            if (cursor1.x>=DDATA_AREA_SIZE_W) cursor1.x=DDATA_AREA_SIZE_W-1;
            if (cursor1.x<0) cursor1.x=0;//however cursor1.x is unsigned I used it for a future possible use of signed value
            break;
        case SET_CURSOR_X2:
            if (a) cursor2.x=param;
            else cursor2.x+=param;

            if (cursor2.x >= DDATA_AREA_SIZE_W) cursor2.x=DDATA_AREA_SIZE_W-1;
            if (cursor2.x<0) cursor2.x=0;
            break;
        case SET_CURSOR_Y1:
            if (a) cursor1.y=param;
            else cursor1.y+=param;

            if (cursor1.y >= DDATA_AREA_SIZE_H ) cursor1.y=DDATA_AREA_SIZE_H-1;
            if (cursor1.y<0) cursor1.y=0;
            break;
        case SET_CURSOR_Y2:
            if (a) cursor2.y=param;
            else cursor2.y+=param;

            if (cursor2.y>=DDATA_AREA_SIZE_W) cursor2.y=DDATA_AREA_SIZE_H-1;
            if (cursor2.y<0) cursor2.y=0;
            break;
    };
    if (cursor1.x > DDATA_AREA_SIZE_W) cursor1.x=DDATA_AREA_SIZE_W;
    if (cursor1.y > DDATA_AREA_SIZE_H) cursor1.y=DDATA_AREA_SIZE_H;
    
    if (cursor2.x > DDATA_AREA_SIZE_W) cursor2.x=DDATA_AREA_SIZE_W;
    if (cursor2.y > DDATA_AREA_SIZE_H) cursor2.y=DDATA_AREA_SIZE_H;
    
    uint16_t variable;
    
    memcpy_P(&variable, &timeValue[curTimeOpt], sizeof(uint16_t));//read data from table placed in program memory
    timeCursor=cursor1.x;
    timeCursor-=cursor2.x;
    timeCursor*=variable;
    timeCursor/=TDIV_W;
    
    memcpy_P(&variable, &voltValue[curVoltOpt], sizeof(uint16_t));
    voltageCursor=cursor1.y;
    voltageCursor-=cursor2.y;
    voltageCursor*=variable;
    voltageCursor/=UDIV_H;
    cli();
    
    upoint8_t temp={TIMECURSOR_VALUE_X,TIMECURSOR_Y};

    
    display_text_PGM(PSTR("       "),TIMECURSOR_VALUE_X,TIMECURSOR_Y);
    if (curTimeOpt<=US_END) displayValue(temp,timeCursor,"us"); //update value on screen
    else if (curTimeOpt>=MS_BEGIN && curTimeOpt<=MS_END) displayValue(temp,timeCursor,"ms"); //update value on screen
    else if (curTimeOpt>=S_BEGIN) displayValue(temp,timeCursor,"s "); //update value on screen
    
    temp.x=VOLTCURSOR_VALUE_X;
    temp.y=VOLTCURSOR_Y;
    display_text_PGM(PSTR("       "),VOLTCURSOR_VALUE_X,VOLTCURSOR_Y);  //erase old values
    displayValue(temp,voltageCursor,"mV");  // print new value

    drawCursors();
    sei();
    
    return 0;
}
